package ast;

import java.util.ArrayList;

/**
 * <code>BuildVisitor</code> Implementation of a Visitor. It traverses the
 * tree creating SymbolTables for scopes.
 */
public class BuildVisitor implements Visitor {
	SymbolTable currentTable;
	SymbolTable rootTable;
	ClassStoreSymbol cs;
	DecStoreSymbol ds;
	MethodStoreSymbol ms;

	public BuildVisitor() {
		rootTable = currentTable = new SymbolTable();
		rootTable.setName("root");
	}

	public Boolean visit(List n) {
		n.left.accept(this);
		if (n.right != null) {
			n.right.accept(this);
		}
		return true;
	}

	public Boolean visit(Node n) {
		n.left.accept(this);
		n.right.accept(this);
		return true;
	}

	public Boolean visit(ClassDecNode n) {
		cs = new ClassStoreSymbol();
		if (!currentTable.duplicatedInstVar(n.id)) {
			currentTable.put(n.id, cs);
		} else {
			System.out.println("CLASS " + n.id
					+ " ALREADY EXSISTS IN THE SYMBOL TABLE!!");
			return false;
		}
		currentTable = currentTable.beginScope();
		currentTable.setName(n.id);
		if (n.left != null) {
			n.left.accept(this);
		}
		currentTable.printTable();
		cs.setPublicSymbols(currentTable.getPublicTable());
		cs.setPrivateSymbols(currentTable.getPrivateTable());
		currentTable = currentTable.endScope();
		return true;
	}

	public Boolean visit(ClassDefNode n) {
		if (n.instList != null) {
			n.instList.accept(this);
		}
		if (n.left != null) {
			n.left.accept(this);
		}
		return true;
	}

	public Boolean visit(InstVarDecNode n) {
		TypeDecNode tdn = (TypeDecNode) n.left;
		TypeDecStoreSymbol tdss = new TypeDecStoreSymbol(tdn.id, n.access,
				tdn.type);

		if (!currentTable.duplicatedInstVar(tdn.id)) {
			currentTable.put(tdn.id, tdss);
			return true;
		} else {
			System.out.println(tdn.id
					+ " IS ALREADY EXSISTING IN THE SYMBOL TABLE!!");
			return false;
		}

	}

	public Boolean visit(MethodDecNode n) {
		ArrayList<TypeDecStoreSymbol> params = paramListToArray(n.params);
		// DUPLICATED METHOD CHECKS
		if (n.id.equalsIgnoreCase(currentTable.getName())) {
			ArrayList<MethodStoreSymbol> list = currentTable.getMethods(n.id);
			if (list != null) {
				for (MethodStoreSymbol m : list) {
					Boolean same = true;
					if (m.params.size() == params.size()) {
						if (m.params.size() != 0) {
							for (int i = 0; i < m.params.size(); i++) {
								if (m.params.get(i).id.equals(params.get(i).id)) {
									if (!m.params.get(i).type.equals(params.get(i).type)) {
										same = false;
									}
								}
							}
						}
					}
					if (same) {
						System.out
								.println("----THE "
										+ n.id
										+ " METHOD IS DUPLICATED WITH SAME EXPRESSION----");
						return false;
					}

					same = false;
				}
			}
		}
		ms = new MethodStoreSymbol(n.access, n.type, n.id, params);
		currentTable.put(n.id, ms);
		currentTable = currentTable.beginScope();
		if (n.params != null) {
			String paramStr = "";
			for (TypeDecStoreSymbol btd : params) {
				currentTable.put(btd.id, new TypeDecStoreSymbol(btd.id, null,
						btd.type));
				paramStr += ":" + btd.type;

			}
			currentTable.setName(n.id + "." + n.access + "." + n.type
					+ paramStr);
		} else {
			currentTable.setName(n.id + "." + n.access + "." + n.type);
		}
		n.left.accept(this);
		return true;
	}

	public Boolean visit(MethodBodyNode n) {
		if (n.vardeclist != null) {
			for (TypeDecStoreSymbol btd : declListToArray(n.vardeclist)) {
				currentTable.put(btd.id, new TypeDecStoreSymbol(btd.id, null,
						btd.type));
			}
		}
		currentTable.printTable();
		currentTable = currentTable.endScope();
		return true;
	}

	public Boolean visit(IfNode n) {
		if (n.e != null) {
			n.e.accept(this);
			if (n.left != null) {
				n.left.accept(this);
			}
			if (n.right != null) {
				n.right.accept(this);
			}
		}
		return true;
	}

	public Boolean visit(BodyNode n) {
		int bc = currentTable.getBodyCount();
		currentTable.incBodyCount();
		currentTable = currentTable.beginScope();
		currentTable.setName(Integer.toString(bc));
		if (n.vardecs != null) {
			for (TypeDecStoreSymbol btd : declListToArray(n.vardecs)) {
				if (!btd.id.equals(currentTable.getName())) {
					currentTable.put(btd.id, new TypeDecStoreSymbol(btd.id,
							null, btd.type));
					currentTable.setName(btd.id);
				} else {
					System.out.println(btd.id
							+ " IS ALREADY EXSISTING IN THE SYMBOL TABLE!!");
				}
			}
		}
		currentTable.printTable();
		currentTable = currentTable.endScope();
		return true;
	}

	public Boolean visit(MainProgramNode n) {
		currentTable = currentTable.beginScope();
		if (n.declist != null) {
			for (TypeDecStoreSymbol btd : declListToArray(n.declist)) {

				if (!btd.id.equals(currentTable.getName())) {
					currentTable.put(btd.id, new TypeDecStoreSymbol(btd.id,
							null, btd.type));
					currentTable.setName(btd.id);
				} else {
					System.out.println(btd.id
							+ " IS ALREADY EXSISTING IN THE SYMBOL TABLE!!");
				}
			}
		}
		if (n.left != null) {
			n.left.accept(this);
		}
		currentTable.setName("start");
		currentTable.printTable();
		currentTable = currentTable.endScope();
		return true;
	}

	public Boolean visit(RepeatNode n) {
		if (n.left != null) {
			n.left.accept(this);
		}
		return true;
	}

	private ArrayList<TypeDecStoreSymbol> paramListToArray(List list) {
		ArrayList<TypeDecStoreSymbol> tmp = new ArrayList<TypeDecStoreSymbol>();
		List tmpList = list;
		ParamNode tdn;
		while (tmpList != null && tmpList.left != null) {
			tdn = (ParamNode) tmpList.left;
			tmp.add(new TypeDecStoreSymbol(tdn.id, null, tdn.type));
			if (tmpList.right == null) {
				break;
			} else {
				tmpList = (List) tmpList.right;
			}
		}
		return tmp;
	}

	private ArrayList<TypeDecStoreSymbol> declListToArray(List list) {
		ArrayList<TypeDecStoreSymbol> tmp = new ArrayList<TypeDecStoreSymbol>();
		List tmpList = list;
		TypeDecNode tdn;
		while (tmpList != null && tmpList.left != null) {
			tdn = (TypeDecNode) tmpList.left;
			tmp.add(new TypeDecStoreSymbol(tdn.id, null, tdn.type));
			if (tmpList.right == null) {
				break;
			} else {
				tmpList = (List) tmpList.right;
			}
		}
		return tmp;
	}

	public Object visit(RootNode n) {
		Boolean left = true;
		Boolean right = true;
		left = (Boolean) n.left.accept(this);
		if (n.right != null) {
			right = (Boolean) n.right.accept(this);
		}
		if (left && right) {
			return rootTable;
		} else {
			return null;
		}
	}

	public Boolean visit(StmtNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ExprNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(AddNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(MinusNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(DivideNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(TimesNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(IntNumNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(RealNumNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(LeNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(LeqNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(GeNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(GeqNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ExpNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(OrNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(AndNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(EqNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(NeqNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(InNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(CharListNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(BooleanNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(AssignNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(VarNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ParamNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(CallVarExprNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(CallVarNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(CallVarStmtNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(LenNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(NotinNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(NotNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ObjTypeDecNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(VarExprNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(BasicTypeDecNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(FormalparamNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ParamlistNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ExprlistNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ParanExprNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(ReturnStmtNode n) {
		// TODO Auto-generated method stub
		return true;
	}

	public Boolean visit(TypeDecNode n) {
		// TODO Auto-generated method stub
		return true;
	}
}